// 跨平台函数封装

#ifndef fz_h
#define fz_h

#include <cstdarg>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <type_traits>
#include <vector>

#ifdef _WIN32
#if defined(_MSC_VER)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif
#else
#define closesocket(x) close(x)
#define INVALID_SOCKET 0
#define SOCKET_ERROR (-1)
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif

struct Color {
  // 属性
  static constexpr const char *reset = "0";      // 重置所有属性
  static constexpr const char *bold = "1";       // 粗体
  static constexpr const char *underscore = "4"; // 下划线
  static constexpr const char *flicker = "5";    // 闪烁
  static constexpr const char *reverse = "7";    // 反向显示
  static constexpr const char *hide = "8";       // 隐藏

  // 前景色
  static constexpr const char *black = "30";     // 黑
  static constexpr const char *red = "31";       // 红
  static constexpr const char *green = "32";     // 绿
  static constexpr const char *yellow = "33";    // 黄
  static constexpr const char *blue = "34";      // 蓝
  static constexpr const char *magenta = "35";   // 品红
  static constexpr const char *cyan_blue = "36"; // 青
  static constexpr const char *white = "37";     // 白

  // 背景色
  static constexpr const char *black_background = "40";     // 黑
  static constexpr const char *red_background = "41";       // 红
  static constexpr const char *green_background = "42";     // 绿
  static constexpr const char *yellow_background = "43";    // 黄
  static constexpr const char *blue_background = "44";      // 蓝
  static constexpr const char *magenta_background = "45";   // 品红
  static constexpr const char *cyan_blue_background = "46"; // 青
  static constexpr const char *white_background = "47";     // 白
};

/**
 * 指定颜色打印，
 * 使用转义字符。
 *
 * @param attribute 属性，参考 Color 枚举，可以是空文本
 * @param prospect 前景色，参考 Color 枚举，可以是空文本
 * @param background 背景色，参考 Color 枚举，可以是空文本
 * @param format 格式化字符串
 * @param ... 参数列表
 */
void ColorPrintfANSI(const char *attribute, const char *prospect,
                     const char *background, const char *format, ...) {
  va_list ap;
  va_start(ap, format);
  vprintf((std::string("\033[") + attribute + ";" + prospect + ";" +
           background + "m" + format + "\033[0m")
              .c_str(),
          ap);
  va_end(ap);
}

/**
 * 指定颜色打印，
 * 使用转义字符。
 *
 * @param attribute 属性，参考 Color 枚举，可以是空文本
 * @param format 格式化字符串
 * @param ... 参数列表
 */
void ColorPrintfANSIAttribute(const char *attribute, const char *format, ...) {
  va_list ap;
  va_start(ap, format);
  vprintf(
      (std::string("\033[") + attribute + ";;m" + format + "\033[0m").c_str(),
      ap);
  va_end(ap);
}

/**
 * 指定颜色打印，
 * 使用转义字符。
 *
 * @param prospect 前景色，参考 Color 枚举，可以是空文本
 * @param format 格式化字符串
 * @param ... 参数列表
 */
void ColorPrintfANSIProspect(const char *prospect, const char *format, ...) {
  va_list ap;
  va_start(ap, format);
  vprintf(
      (std::string("\033[;") + +prospect + ";m" + format + "\033[0m").c_str(),
      ap);
  va_end(ap);
}

/**
 * 指定颜色打印，
 * 使用转义字符。
 *
 * @param background 背景色，参考 Color 枚举，可以是空文本
 * @param format 格式化字符串
 * @param ... 参数列表
 */
void ColorPrintfANSIBackground(const char *background, const char *format,
                               ...) {
  va_list ap;
  va_start(ap, format);
  vprintf(
      (std::string("\033[;;") + background + "m" + format + "\033[0m").c_str(),
      ap);
  va_end(ap);
}

/**
 * 读取所有
 *
 * @param stream 流
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @return 字节数组
 */
std::vector<char> ReadAll(FILE *stream, long offset = 0,
                          int origin = SEEK_SET) {
  auto current = ftell(stream);
  if (fseek(stream, 0, SEEK_END))
    return {};
  auto size = ftell(stream) - current;
  std::vector<char> result;
  result.resize(size);
  if (fseek(stream, offset, origin))
    return {};
  fread((void *)result.data(), size, 1, stream);
  return result;
}

/**
 * 尝试读取所有，
 * 如果读取失败，则抛出异常。
 *
 * @param stream 流
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 字节数组
 */
std::vector<char> TryReadAll(FILE *stream, long offset = 0,
                             int origin = SEEK_CUR,
                             const std::string &mode = "rb") {
  auto current = ftell(stream);
  if (fseek(stream, 0, SEEK_END))
    throw "open file failed";
  auto size = ftell(stream) - current;
  std::vector<char> result;
  result.resize(size);
  if (fseek(stream, offset, origin))
    throw "open file failed";
  fread((void *)result.data(), size, 1, stream);
  return result;
}

/**
 * 读取所有
 *
 * @param file 文件
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 字节数组
 */
std::vector<char> ReadAll(const std::string &file, long offset = 0,
                          int origin = SEEK_CUR,
                          const std::string &mode = "rb") {
  auto stream = fopen(file.c_str(), mode.c_str());
  if (!stream) {
    return {};
  }
  return ReadAll(stream, origin);
}

/**
 * 尝试读取所有，
 * 如果读取失败，则抛出异常。
 *
 * @param file 文件
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 字节数组
 */
std::vector<char> TryReadAll(const std::string &file, long offset = 0,
                             int origin = SEEK_CUR,
                             const std::string &mode = "rb") {
  auto stream = fopen(file.c_str(), mode.c_str());
  if (!stream) {
    throw "open file failed";
  }
  auto current = ftell(stream);
  if (fseek(stream, 0, SEEK_END))
    throw "open file failed";
  auto size = ftell(stream) - current;
  std::vector<char> result;
  result.resize(size);
  if (fseek(stream, offset, origin))
    throw "open file failed";
  fread((void *)result.data(), size, 1, stream);
  return result;
}

/**
 * 读取所有字符串
 *
 * @param stream 流
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @return 字符串
 */
std::string ReadAllString(FILE *stream, long offset = 0,
                          int origin = SEEK_CUR) {
  auto current = ftell(stream);
  if (fseek(stream, 0, SEEK_END))
    return {};
  auto size = ftell(stream) - current;
  std::string result;
  result.resize(size);
  if (fseek(stream, offset, origin))
    return {};
  fread((void *)result.data(), size, 1, stream);
  return result;
}

/**
 * 尝试读取所有字符串，
 * 如果读取失败，则抛出异常。
 *
 * @param stream 流
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 字符串
 */
std::string TryReadAllString(FILE *stream, long offset = 0,
                             int origin = SEEK_CUR,
                             const std::string &mode = "rb") {
  auto current = ftell(stream);
  if (fseek(stream, 0, SEEK_END))
    throw "open file failed";
  auto size = ftell(stream) - current;
  std::string result;
  result.resize(size);
  if (fseek(stream, offset, origin))
    throw "open file failed";
  fread((void *)result.data(), size, 1, stream);
  return result;
}

/**
 * 读取所有字符串
 *
 * @param file 文件
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 字符串
 */
std::string ReadAllString(const std::string &file, long offset = 0,
                          int origin = SEEK_CUR,
                          const std::string &mode = "rb") {
  auto stream = fopen(file.c_str(), mode.c_str());
  if (!stream) {
    return {};
  }
  return ReadAllString(stream, origin);
}

/**
 * 尝试读取所有字符串，
 * 如果读取失败，则抛出异常。
 *
 * @param file 文件
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 字符串
 */
std::string TryReadAllString(const std::string &file, long offset = 0,
                             int origin = SEEK_CUR,
                             const std::string &mode = "rb") {
  auto stream = fopen(file.c_str(), mode.c_str());
  if (!stream) {
    throw "open file failed";
  }
  auto current = ftell(stream);
  if (fseek(stream, 0, SEEK_END))
    throw "open file failed";
  auto size = ftell(stream) - current;
  std::string result;
  result.resize(size);
  if (fseek(stream, offset, origin))
    throw "open file failed";
  fread((void *)result.data(), size, 1, stream);
  return result;
}

/**
 * @brief 写所有
 *
 * @param stream 流
 * @param value 值
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @return 如果写失败，则返回 false
 */
bool WriteAll(FILE *stream, const std::vector<char> &value, long offset = 0,
              int origin = SEEK_CUR) {
  return !fseek(stream, offset, origin) &&
         fwrite(value.data(), value.size(), 1, stream) == 1;
}

/**
 * @brief 写所有
 *
 * @param file 文件
 * @param value 值
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 如果写失败，则返回 false
 */
bool WriteAll(const std::string &file, const std::vector<char> &value,
              long offset = 0, int origin = SEEK_CUR,
              const std::string &mode = "wb") {
  auto stream = fopen(file.c_str(), mode.c_str());
  return !fseek(stream, offset, origin) &&
         fwrite(value.data(), value.size(), 1, stream) == 1;
}

/**
 * @brief 写所有
 *
 * @param stream 流
 * @param value 值
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @return 如果写失败，则返回 false
 */
bool WriteAll(FILE *stream, const std::string &value, long offset = 0,
              int origin = SEEK_CUR) {
  return !fseek(stream, offset, origin) &&
         fwrite(value.data(), value.size(), 1, stream) == 1;
}

/**
 * 写所有
 *
 * @param file 文件
 * @param value 值
 * @param offset 偏移
 * @param origin 起始位置，参考 SEEK 枚举
 * @param mode 打开方式
 * @return 如果写失败，则返回 false
 */
bool WriteAll(const std::string &file, const std::string &value,
              long offset = 0, int origin = SEEK_CUR,
              const std::string &mode = "wb") {
  auto stream = fopen(file.c_str(), mode.c_str());
  return !fseek(stream, offset, origin) &&
         fwrite(value.data(), value.size(), 1, stream) == 1;
}

/**
 * 打印
 *
 * @param arg 参数
 */
template <typename T> void Println(const T &arg) {
  std::cout << arg << std::endl;
}

/**
 * 打印,
 * 参数之间会有一个空格分隔。
 *
 * @param arg 参数
 * @param args 参数列表
 */
template <class T, class... Args> void Println(const T &arg, Args... args) {
  std::cout << arg << " ";
  Println(args...);
}

/**
 * 删首尾空，
 * 即删除首部和尾部的空白文本。
 *
 * @param text 文本
 * @param whitespace 空白文本
 * @return 文本
 */
std::string Trim(const std::string &text,
                 const std::string &whitespace = " \t") {
  auto begin = text.find_first_not_of(whitespace);
  if (std::string::npos == begin) {
    return "";
  }
  auto end = text.find_last_not_of(whitespace);
  auto range = end - begin + 1;
  return text.substr(begin, range);
}

/**
 * 分割文本
 *
 * @param text 文本
 * @param delimiters 分隔符
 * @return 文本数组
 */
std::vector<std::string> Split(const std::string &text,
                               const std::string &delimiters) {
  std::vector<std::string> result;
  std::string::size_type last = text.find_first_not_of(delimiters, 0);
  std::string::size_type p = text.find_first_of(delimiters, last);
  while (std::string::npos != p || std::string::npos != last) {
    result.push_back(text.substr(last, p - last));
    last = text.find_first_not_of(delimiters, p);
    p = text.find_first_of(delimiters, last);
  }
  return result;
}

/**
 * 这个类实现了异步读写 ini 文件。
 * 其 Get 和 Set 操作都是在内存进行的，
 * 只有调用 Load 和 Store 时才会触发 IO。
 * 不会检查 ini 文件的语法。
 * 由于 map 容器的特性，
 * 将会自动排序节和键值。
 *
 * 例子:
 *
 * Config config("test.ini");
 * config.Load();
 * config.Get("user","name","");
 * config.Set("user","name","value");
 * config.Store();
 *
 * 如果以 bool 的形式获取值，
 * 将会自动识别并转换 false 和 0 值。
 * 例如:
 *
 * [user]
 * name=0
 *
 * Get("user","name",false) 返回 false
 *
 * [user]
 * name=123
 *
 * Get("user","name",false) 返回 true
 *
 * [user]
 * name=true
 *
 * Get("user","name",false) 返回 true
 */
class Config {
public:
  Config() {}

  /**
   * 映射到文件
   *
   * @param file 文件
   */
  Config(const std::string &file) { this->file = file; }

  /*
   * 从文件加载
   *
   * @return 如果加载失败，则返回 false
   */
  bool Load() {
    std::string text;
    try {
      text = TryReadAllString(file, 0, 0, "r");
    } catch (...) {
      return false;
    }
    return Load(text);
  }

  /**
   * 从文本加载
   *
   * @param text ini 格式文本
   * @return 如果加载失败，则返回 false
   */
  bool Load(std::string text) {
    std::string name;
    auto lines = Split(text, "\n");
    for (const auto &i : lines) {
      text = Trim(i);
      if (text != "") {
        if (text[0] == '[') {
          auto n = text.find("]", 1);
          if (n != -1) {
            name = text.substr(1, n - 1);
          }
        } else {
          auto t = text.find("=");
          if (t != -1 && t != 0 && t + 1 < text.size()) {
            Set(name, Trim(text.substr(0, t)), text.substr(t + 1));
          }
        }
      }
    }
    return true;
  }

  /**
   * 储存到文件
   *
   * @return 如果储存失败，则返回 false
   */
  bool Store() { return WriteAll(file, StoreString()); }

  /**
   * 储存到文本
   *
   * @return ini 格式文本
   */
  std::string StoreString() {
    std::string result;
    for (const auto &i : table) {
      result += "[" + i.first + "]\n";
      for (const auto &n : i.second) {
        result += n.first + "=" + n.second + "\n";
      }
    }
    return result;
  }

  /**
   * 获取值
   *
   * @param subsection 节
   * @param key 键
   * @param defaultValue 默认值
   * @return 如果节或键不存在，则返回 defaultValue
   */
  std::string Get(const std::string &subsection, const std::string &key,
                  const std::string &defaultValue = "") {
    if (subsection == "" || key == "") {
      return "";
    }
    auto i = table.find(subsection);
    if (i != table.end()) {
      auto t = i->second.find(key);
      if (t != i->second.end()) {
        return t->second;
      }
    }
    return defaultValue;
  }

  /**
   * 获取值
   *
   * @param subsection 节
   * @param key 键
   * @param defaultValue 默认值
   * @return 如果节或键不存在，则返回 defaultValue
   */
  bool Get(const std::string &subsection, const std::string &key,
           bool defaultValue) {
    if (subsection == "" || key == "") {
      return defaultValue;
    }
    auto i = table.find(subsection);
    if (i != table.end()) {
      auto t = i->second.find(key);
      if (t != i->second.end()) {
        return !(t->second == "false" || t->second == "0");
      }
    }
    return defaultValue;
  }

  /**
   * 获取值
   *
   * @param subsection 节
   * @param key 键
   * @param defaultValue 默认值
   * @return 如果节或键不存在，则返回 defaultValue
   */
  int Get(const std::string &subsection, const std::string &key,
          int defaultValue) {
    if (subsection == "" || key == "") {
      return defaultValue;
    }
    auto i = table.find(subsection);
    if (i != table.end()) {
      auto t = i->second.find(key);
      if (t != i->second.end()) {
        return std::atoi(t->second.c_str());
      }
    }
    return defaultValue;
  }

  /**
   * 获取值
   *
   * @param subsection 节
   * @param key 键
   * @param defaultValue 默认值
   * @return 如果节或键不存在，则返回 defaultValue
   */
  long long Get(const std::string &subsection, const std::string &key,
                long long defaultValue) {
    if (subsection == "" || key == "") {
      return defaultValue;
    }
    auto i = table.find(subsection);
    if (i != table.end()) {
      auto t = i->second.find(key);
      if (t != i->second.end()) {
        return std::atoll(t->second.c_str());
      }
    }
    return defaultValue;
  }

  /**
   * 获取值
   *
   * @param subsection 节
   * @param key 键
   * @param defaultValue 默认值
   * @return 如果节或键不存在，则返回 defaultValue
   */
  double Get(const std::string &subsection, const std::string &key,
             double defaultValue) {
    if (subsection == "" || key == "") {
      return defaultValue;
    }
    auto i = table.find(subsection);
    if (i != table.end()) {
      auto t = i->second.find(key);
      if (t != i->second.end()) {
        return std::atof(t->second.c_str());
      }
    }
    return defaultValue;
  }

  /**
   * 设置值
   *
   * @param subsection 节
   * @param key 键
   * @param value 值
   * @return 如果设置失败，则返回 false
   */
  bool Set(const std::string &subsection, const std::string &key,
           const std::string &value) {
    if (subsection == "" || key == "") {
      return false;
    }
    table.emplace(subsection, std::map<std::string, std::string>())
        .first->second[key] = value;
    return true;
  }

  /**
   * 设置值
   *
   * @param subsection 节
   * @param key 键
   * @param value 值
   * @return 如果设置失败，则返回 false
   */
  bool Set(const std::string &subsection, const std::string &key, bool value) {
    if (subsection == "" || key == "") {
      return false;
    }
    table.emplace(subsection, std::map<std::string, std::string>())
        .first->second[key] = value ? "true" : "false";
    return true;
  }

  /**
   * 设置值
   *
   * @param subsection 节
   * @param key 键
   * @param value 值
   * @return 如果设置失败，则返回 false
   */
  bool Set(const std::string &subsection, const std::string &key, int value) {
    if (subsection == "" || key == "") {
      return false;
    }
    table.emplace(subsection, std::map<std::string, std::string>())
        .first->second[key] = std::to_string(value);
    return true;
  }

  /**
   * 设置值
   *
   * @param subsection 节
   * @param key 键
   * @param value 值
   * @return 如果设置失败，则返回 false
   */
  bool Set(const std::string &subsection, const std::string &key,
           long long value) {
    if (subsection == "" || key == "") {
      return false;
    }
    table.emplace(subsection, std::map<std::string, std::string>())
        .first->second[key] = std::to_string(value);
    return true;
  }

private:
  // 文件
  std::string file;

  // <节,<键,值>>
  std::map<std::string, std::map<std::string, std::string>> table;
};

/**
 * 套接字，
 * 使用 g++ 编译需要添加参数 -lws2_32，
 * 例如:
 *
 * g++ main.cpp -lws2_32
 *
 * 参考:
 * https://www.cnblogs.com/jianqiang2010/archive/2010/08/20/1804598.html
 */
struct Socket {
public:
  // 地址簇协议
  enum class Family {
    IPv4 = AF_INET,  // IPv4
    IPv6 = AF_INET6, // IPv6
    Any = AF_UNSPEC, // 协议无关
  };

  // 套接字类型
  enum class SocketType {
    Stream = SOCK_STREAM,  // 流
    Datagram = SOCK_DGRAM, // 数据报
  };

  // 通信协议
  enum class ProtocolType {
    IP = IPPROTO_IP,     // IP 协议
    IPv4 = IPPROTO_IPV4, // IPv4
    IPv6 = IPPROTO_IPV6, // IPv6
    Udp = IPPROTO_UDP,   // UDP
    Tcp = IPPROTO_TCP,   // TCP
  };

  /**
   * 创建套接字，
   * 如果创建失败，则抛出异常
   *
   * @param family 地址簇协议
   * @param type 套接字类型
   * @param protocol 通信协议
   */
  Socket(Family family, SocketType type, ProtocolType protocol) {
    // https://baike.baidu.com/item/getaddrinfo/9021771?fr=aladdin
    ai_family = (int)family;
    ai_sock_type = (int)type;
    ai_protocol = (int)protocol;
    if (!init_socket()) {
      throw "socket create failed";
    }
  }

  /**
   * 设置发送的超时时间，
   * 建立连接后才能设置。
   *
   * @param timeout 超时时间，单位毫秒，0 表示无限等待
   */
  void SetSendTimeout(int timeout) {
    this->send_timeout = timeout;
    set_socket_timeout();
  }

  /**
   * 设置接收的超时时间
   * 建立连接后才能设置。
   *
   * @param timeout 超时时间，单位毫秒，0 表示无限等待
   */
  void SetReceiveTimeout(int timeout) {
    this->receive_timeout = timeout;
    set_socket_timeout();
  }

  /**
   * 绑定
   *
   * @param host 主机
   * @param port 端口
   * @return 如果绑定失败，则返回 false
   */
  bool Bind(const std::string &host, int port) {
    addrinfo *info;
    addrinfo ai;
    memset(&ai, 0, sizeof(addrinfo));

    ai.ai_family = ai_family;
    ai.ai_socktype = ai_sock_type;
    ai.ai_protocol = ai_protocol;
    ai.ai_flags = AI_PASSIVE; // 根据 ai_family 自动填充 ai_addr 和 ai_addrlen

    // https://docs.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-addrinfoa
    if (getaddrinfo(host.c_str(), std::to_string(port).c_str(), &ai, &info)) {
      return false;
    }

    // 循环绑定 getaddrinfo 的返回值，直到成功或耗尽
    addrinfo *i;
    for (i = info; i; i = i->ai_next) {
      socket_descriptor = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
      if (socket_descriptor == INVALID_SOCKET ||
          bind(socket_descriptor, i->ai_addr, i->ai_addrlen) < 0) {
        continue;
      }
      break;
    }

    freeaddrinfo(info);

    if (!i) {
      return false;
    }

    return true;
  }

  /**
   * 监听
   *
   * @param backlog 连接队列的长度
   */
  bool Listen(int backlog = SOMAXCONN) {
    return !listen(socket_descriptor, backlog);
  }

  /**
   * 同意，
   * 在此之前需要调用 Listen 函数，
   * 如果同意失败，则抛出异常。
   *
   * @return 套接字
   */
  Socket Accept() {
    sockaddr_storage storage;
    socklen_t len = sizeof(storage);
    auto client = accept(socket_descriptor, (sockaddr *)&storage, &len);
    if (!client)
      throw "accept failed";
    auto temp = Socket(client);
    if (getnameinfo((sockaddr *)&storage, len, temp.address,
                    sizeof(temp.address), 0, 0, NI_NUMERICHOST)) {
      strcpy(temp.address, "unknown");
    }
    return temp;
  }

  /**
   * 连接
   *
   * @param host 主机
   * @param port 端口
   * @param timeout 超时时间，单位毫秒，0 表示无限等待
   * @return 如果连接失败，则返回 false
   */
  bool Connect(const std::string &host, int port, int timeout = 20000) {
    addrinfo *info;
    addrinfo ai;
    memset(&ai, 0, sizeof(addrinfo));

    ai.ai_family = ai_family;
    ai.ai_socktype = ai_sock_type;
    ai.ai_protocol = ai_protocol;
    ai.ai_flags = AI_PASSIVE; // 根据 ai_family 自动填充 ai_addr 和 ai_addrlen

    // https://docs.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-addrinfoa
    // https://baike.baidu.com/item/getaddrinfo/9021771?fr=aladdin
    if (getaddrinfo(host.c_str(), std::to_string(port).c_str(), &ai, &info)) {
      return false;
    }

    // 循环连接 getaddrinfo 的返回值，直到成功或耗尽
    addrinfo *i;
    for (i = info; i; i = i->ai_next) {
      socket_descriptor = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
      if (socket_descriptor == INVALID_SOCKET) {
        continue;
      }

      // 将套接字设置成非堵塞模式，以支持自定义超时时间
      if (!set_socket_block(false)) {
        continue;
      }

      // 非堵塞模式下调用 connect 时会返回 -1
      auto result = connect(socket_descriptor, i->ai_addr, i->ai_addrlen);

#ifdef _WIN32
      if (result == -1 && result != WSAEWOULDBLOCK) {
        continue;
      }
#else
      if (ret < 0 && errno != EINPROGRESS) {
        continue;
      }
#endif
      if (!result) {
        break;
      }

      // 等待连接
      timeval tv;
      tv.tv_sec = 0;
      tv.tv_usec = timeout * 1000;
      fd_set set;
      FD_ZERO(&set);
      FD_SET(socket_descriptor, &set);

      // https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select
      result = select(socket_descriptor + 1, 0, &set, 0, &tv);
      if (result <= 0) {
        continue;
      }

      // 判断是否已连接
      socklen_t temp = sizeof(result);
      if (getsockopt(socket_descriptor, SOL_SOCKET, SO_ERROR, (char *)&result,
                     &temp) ||
          result) {
        continue;
      }

      break;
    }

    freeaddrinfo(info);

    if (!i) {
      return false;
    }

    // 设置发送和接收的超时时间
    set_socket_timeout();

    // 重新设置成堵塞模式
    if (!set_socket_block(true)) {
      closesocket(socket_descriptor);
      return false;
    }

    if (getnameinfo(i->ai_addr, i->ai_addrlen, address, sizeof(address), NULL,
                    0, NI_NUMERICHOST)) {
      strcpy(address, "unknown");
    }

    return true;
  }

  /**
   * 关闭
   *
   * 如果关闭失败，则返回 false
   */
  bool Close() { return !closesocket(socket_descriptor); }

  /**
   * 发送
   *
   * @param data 数据
   * @param size 数据的字节数
   * @return 发送成功的字节数，-1 表示发送失败
   */
  ssize_t Send(const char *data, size_t size) {
    return send(socket_descriptor, data, size, 0);
  }

  /**
   * 发送
   *
   * @param data 数据
   * @return 如果所有字节都发送成功，则返回 true，返回 true 不代表对方接收成功
   */
  bool Send(const std::vector<char> &data) {
    return Send(data.data(), data.size()) == data.size();
  }

  /**
   * 发送
   *
   * @param data 数据
   * @return 如果所有字节都发送成功，则返回 true，返回 true 不代表对方接收成功
   */
  bool Send(const char *data) {
    auto size = strlen(data);
    return Send(data, size) == size;
  }

  /**
   * 发送
   *
   * @param data 数据
   * @return 如果所有字节都发送成功，则返回 true，返回 true 不代表对方接收成功
   */
  bool Send(const std::string &data) {
    return Send(data.data(), data.size()) == data.size();
  }

  /**
   * 发送
   *
   * @param data 数据
   * @return 如果所有字节都发送成功，则返回 true，返回 true 不代表对方接收成功
   */
  template <typename T> bool Send(const T &data) {
    return Send((const char *)&data, sizeof(data)) == sizeof(T);
  }

  /**
   * 接收
   *
   * @param buffer 缓冲区
   * @param size 缓冲区的字节数
   * @return 接收成功的字节数，-1 表示接收失败
   */
  ssize_t Receive(char *buffer, size_t size) {
    return recv(socket_descriptor, buffer, size, 0);
  }

private:
  // 底层的套接字
  int socket_descriptor;

  // 地址簇协议
  int ai_family;

  // 套接字类型
  int ai_sock_type;

  // 通信协议
  int ai_protocol;

  // 发送的超时时间
  int send_timeout;

  // 接收的超时时间
  int receive_timeout;

  // TODO: 这个有鸟用？
  // 是否堵塞
  bool is_block;

  // 地址
  char address[INET6_ADDRSTRLEN];

  /**
   * 创建套接字，
   * 如果创建失败，则抛出异常
   *
   * @param socket_descriptor 底层的套接字
   */
  Socket(int socket_descriptor) {
    // https://baike.baidu.com/item/getaddrinfo/9021771?fr=aladdin
    this->socket_descriptor = socket_descriptor;
    if (socket_descriptor < 0) {
      throw "socket create failed";
    }
  }

  /**
   * 初始化套接字
   */
  bool init_socket() {
#ifdef _WIN32
    static WSADATA wsaData = WSAData();
    return !WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
    return true;
  }

  /**
   * 设置套接字的 IO 模式
   *
   * @param block 堵塞
   * @return 如果失败，则返回 false
   */
  bool set_socket_block(bool block) {
    this->is_block = block;
#ifdef WIN32
    unsigned long temp = block ? 0 : 1;
    return !ioctlsocket(socket_descriptor, FIONBIO, &temp);
#else
    int flag = fcntl(socket_descriptor, F_GETFL, 0);
    if (flag < 0) {
      return false;
    }
    if (fcntl(socket_descriptor, F_SETFL,
              block ? flag | O_NONBLOCK : flag ^ O_NONBLOCK) < 0) {
      return false;
    }
#endif
    return true;
  }

  /**
   * 设置套接字的发送和接收的超时时间，
   * 建立连接后才能设置。
   */
  void set_socket_timeout() {
    int one = 1;
#ifdef _WIN32
    // 禁用 Nagle 算法，我也不知道为什么要这么做
    setsockopt(socket_descriptor, IPPROTO_TCP, TCP_NODELAY, (char *)&one,
               sizeof(one));
    setsockopt(socket_descriptor, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&one,
               sizeof(one));
    // 设置发送超时时间
    DWORD temp = send_timeout;
    setsockopt(socket_descriptor, SOL_SOCKET, SO_SNDTIMEO, (const char *)&temp,
               sizeof(temp));
    // 设置接收超时时间
    temp = receive_timeout;
    setsockopt(socket_descriptor, SOL_SOCKET, SO_RCVTIMEO, (const char *)&temp,
               sizeof(temp));
#else
    // 禁用 Nagle 算法，我也不知道为什么要这么做
    setsockopt(socket_descriptor, SOL_TCP, TCP_NODELAY, (char *)&one,
               sizeof(one));
    // 设置发送超时时间
    struct timeval tv = {};
    tv.tv_sec = send_timeout / 1000;
    tv.tv_usec = (send_timeout % 1000) * 1000;
    setsockopt(socket_descriptor, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv,
               sizeof(tv));
    // 设置接收超时时间
    tv.tv_sec = receive_timeout / 1000;
    tv.tv_usec = (receive_timeout % 1000) * 1000;
    setsockopt(socket_descriptor, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv,
               sizeof tv);
#endif
  }
};

/**
 * Tcp 协议的套接字，
 * 使用 g++ 编译需要添加参数 -lws2_32，
 * 例如:
 *
 * g++ main.cpp -lws2_32
 */
struct TcpSocket {
  /**
   * 数据到达
   *
   * @param data 数据
   */
  void (*OnData)(const char *data, size_t size);

  /**
   * 发送
   *
   * @param data 数据
   * @return 如果所有字节都发送成功，则返回 true，返回 true 不代表对方接收成功
   */
  bool Send(const char *data, size_t size) { return socket.Send(data, size); }

  /**
   * 同步发送，
   * 发送后堵塞，直至对方接收成功或超时。
   *
   * @param data 数据
   * @param result 结果
   * @param timeout 超时时间，单位毫秒，0 表示无限等待
   * @return 1 表示对方接收成功，0 表示对方接收失败，-1 表示超时
   */
  int SynchronizedSend(const std::vector<char> &data, std::vector<char> &result,
                       int timeout = 20000) {}

  /**
   * 连接
   *
   * @param host 主机
   * @param port 端口
   * @param timeout 超时时间，单位毫秒，0 表示无限等待
   * @return 如果连接失败，则返回 false
   */
  bool Connect(const std::string &host, int port, int timeout = 20000) {
    return socket.Connect(host, port, timeout);
  }

  TcpSocket() {}

  TcpSocket(const std::string &host, int port, int timeout = 20000) {
    if (!socket.Connect(host, port, timeout)) {
      throw "socket connect failed";
    }
  }

private:
  Socket socket = Socket(Socket::Family::Any, Socket::SocketType::Stream,
                         Socket::ProtocolType::Tcp);
};

/**
 * Tcp 协议的监听，
 * 使用 g++ 编译需要添加参数 -lws2_32，
 * 例如:
 *
 * g++ main.cpp -lws2_32
 */
struct TcpListener {};

#endif